using System;
using System.Collections.Generic;
using System.Text;

namespace XnaDevRu.Bullet
{
	public abstract class OverlappingPairCache : IBroadphase
	{
		private static int _overlappingPairCount = 0;
		private List<BroadphasePair> _overlappingPairs = new List<BroadphasePair>();
		//during the dispatch, check that user doesn't destroy/create proxy
		private bool _blockedForChanges;

		public List<BroadphasePair> OverlappingPairs { get { return _overlappingPairs; } set { _overlappingPairs = value; } }
		public bool BlockedForChanges { get { return _blockedForChanges; } set { _blockedForChanges = value; } }

		public static int OverlappingPairCount { get { return _overlappingPairCount; } set { _overlappingPairCount = value; } }

		public void RemoveOverlappingPair(BroadphasePair pair)
		{
			_overlappingPairs.Remove(pair);
		}

		public void AddOverlappingPair(BroadphaseProxy proxyA, BroadphaseProxy proxyB)
		{
			//don't add overlap with own
			bool test = proxyA != proxyB;
            BulletDebug.Assert(proxyA != proxyB);

			if (!NeedsBroadphaseCollision(proxyA, proxyB))
				return;

			BroadphasePair pair = new BroadphasePair(proxyA, proxyB);
			_overlappingPairs.Add(pair);
			_overlappingPairCount++;
		}

		//this FindPair becomes really slow. Either sort the list to speedup the query, or
		//use a different solution. It is mainly used for Removing overlapping pairs. Removal could be delayed.
		//we could keep a linked list in each proxy, and store pair in one of the proxies (with lowest memory address)
		//Also we can use a 2D bitmap, which can be useful for a future GPU implementation
		public BroadphasePair FindPair(BroadphaseProxy proxyA, BroadphaseProxy proxyB)
		{
			for (int i = _overlappingPairs.Count - 1; i >= 0; i--)
			{
				BroadphasePair pair = _overlappingPairs[i];
				if (((pair.ProxyA == proxyA) && (pair.ProxyB== proxyB)) ||
					((pair.ProxyA == proxyB) && (pair.ProxyB == proxyA)))
				{
					return pair;
				}
			}

            return null;
		}

		public void CleanProxyFromPairs(BroadphaseProxy proxy)
		{
			for (int i = 0; i < _overlappingPairs.Count; i++)
			{
				BroadphasePair pair = _overlappingPairs[i];
				if (pair.ProxyA == proxy ||
					pair.ProxyB == proxy)
				{
                    CleanOverlappingPair(ref pair);
                    _overlappingPairs[i] = pair;
				}
			}
		}

		public void RemoveOverlappingPairsContainingProxy(BroadphaseProxy proxy)
		{
			List<BroadphasePair> delete = new List<BroadphasePair>();

			for (int i = _overlappingPairs.Count - 1; i >= 0; i--)
			{
				BroadphasePair pair = _overlappingPairs[i];
				if (pair.ProxyA == proxy ||
					pair.ProxyB == proxy)
				{
					RemoveOverlappingPair(pair);
					i++;
				}
			}

			foreach ( BroadphasePair pair in delete ) RemoveOverlappingPair( pair );
		}

		public bool NeedsBroadphaseCollision(BroadphaseProxy proxy0, BroadphaseProxy proxy1)
		{
			bool collides = (proxy0.CollisionFilterGroup & proxy1.CollisionFilterMask) != 0;
			collides = collides && ((proxy1.CollisionFilterGroup & proxy0.CollisionFilterMask) != 0);

			return collides;
		}

		public void ProcessAllOverlappingPairs(IOverlapCallback callback)
		{
			List<BroadphasePair> deleting = new List<BroadphasePair>();
            for (int i = 0; i < _overlappingPairs.Count; i++)
			{
                BroadphasePair p = _overlappingPairs[i];
				if (callback.ProcessOverlap(ref p))
				{
                    CleanOverlappingPair(ref p);
					deleting.Add(p);
					_overlappingPairCount--;
				}
			}

			for (int i = 0; i < deleting.Count; i++)
				_overlappingPairs.Remove(deleting[i]);
		}

		public void CleanOverlappingPair(ref BroadphasePair pair)
		{
            if (pair.CollisionAlgorithm != null)
            {
				if (pair.CollisionAlgorithm is IDisposable)
					(pair.CollisionAlgorithm as IDisposable).Dispose();
                pair.CollisionAlgorithm = null;
            }
		}

		public abstract void RefreshOverlappingPairs();

		#region IBroadphase Members
		public abstract BroadphaseProxy CreateProxy(Microsoft.Xna.Framework.Vector3 min, Microsoft.Xna.Framework.Vector3 max, BroadphaseNativeTypes shapeType, object userData, BroadphaseProxy.CollisionFilterGroups collisionFilterGroup, BroadphaseProxy.CollisionFilterGroups collisionFilterMask);

		public abstract void DestroyProxy(BroadphaseProxy proxy);

		public abstract void SetAabb(BroadphaseProxy proxy, Microsoft.Xna.Framework.Vector3 aabbMin, Microsoft.Xna.Framework.Vector3 aabbMax);

		#endregion
	}
}
